package com.superatom.concurrent.share;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * volatile与同步：
 * 在非volatile域上的原子操作不比刷新到主存中去，因此其他其他读取该域的任务也不必看到这个新值。
 * 如果多个任务在同时访问该域，那么这个域就必须是volatile的，否则，这个域就只能经由同步来访问。
 * 同步也会导致向主存中刷新，因此如果一个域完全由synchronized方法或语句块来保护，那就不必设置为synchronized了。
 *
 * 使用volatile而不是synchronized的唯一安全的情况是类中只有一个可变的域。再次提醒，你的第一选择应该是synchronized。
 *
 * volatile关键字的两个作用：
 * 1、把修改后的值马上刷新到主存中
 * 2、告诉编译器不要执行任何移除读取或写入操作的优化，目的就是用线程中的局部变量维护对这个域的精确同步
 */
public class AtomicityTest implements Runnable {
    private int i = 0;

    /**
     * 尽管return i是原子性操作，但是缺少同步使得其数值可以在处于不稳定的中间状态时被读取
     * 所以还是要使用synchronized
     *
     * 对于windows这个方法不加synchronized关键字不会有线程问题
     * 对于Mac可以发现线程问题
     * @return
     */
    public /*synchronized*/ int getValue(){
        return i;
    }

    /**
     * 在Java中，递增不是一个原子性操作，涉及到读出与写入，为了不让多个线程对同一个资源进行操作
     * 使用了synchronized关键字，同步会导致向主存中刷新。（相当于volatile的其中一个特性）
     */
    private synchronized void evenIncrement(){
        i++;
        i++;
    }

    public void run() {
        while (true){
            evenIncrement();
        }
    }

    public static void main(String[] args) {
        ExecutorService exec = Executors.newCachedThreadPool();

        AtomicityTest at = new AtomicityTest();
        for (int i = 0; i < 10; i++) {
            exec.execute(at);
        }

        while (true){
            int val = at.getValue();
            if (val % 2 != 0){
                System.out.println(val);
                System.exit(0);
            }
        }
    }
}
